﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Linq.Expressions;

namespace Test
{
	/// <summary>
	/// rewrites nodes with ConstantExpressions, and doesn't try to create a ConstantExpression until one is needed.
	/// </summary>
	public class DeferredLambdaInvokerRewriter : ExpressionVisitor
	{
		Expression root;
		IEnumerable<LambdaExpression> lambdas;
		Dictionary<Type, LambdaExpression> map;
		public DeferredLambdaInvokerRewriter(Expression root, IEnumerable<LambdaExpression> lambdas) {
			this.lambdas = lambdas;
			this.root = root;
			this.map = new Dictionary<Type, LambdaExpression>();
			foreach (var l in this.lambdas) {
				Type elementType = TypeHelper.GetElementType(l.Body.Type); //TypeHelper.GetElementType(l.ReturnType);
				this.map.Add(elementType, l);
			}
		}

		public Expression Rewrite() {
			return this.Visit(this.root);
		}

		public override Expression Visit(Expression node) {
			return base.Visit(node);
		}

		protected override ElementInit VisitElementInit(ElementInit node) {
			return base.VisitElementInit(node);
		}

		protected override Expression VisitNew(NewExpression node) {
			Type elementType;
			if (this.IsQuery(node, out elementType)) {
				//LambdaExpression lambda = Expression.Lambda(node);
				//object instance = lambda.Compile().DynamicInvoke(new object[0]);
				//return Expression.Constant(instance);
				LambdaExpression lambda = this.map[elementType];
				object value = lambda.Compile().DynamicInvoke();
				IQueryable replacement = ((System.Collections.IEnumerable)value).AsQueryable();
				return Expression.Constant(replacement);
			}
			return base.VisitNew(node);
		}


		protected override Expression VisitConstant(ConstantExpression node) {
			Type elementType;
			if (this.IsQuery(node, out elementType)) {
				LambdaExpression lambda = this.map[elementType];
				object value = lambda.Compile().DynamicInvoke();
				IQueryable replacement = ((System.Collections.IEnumerable)value).AsQueryable();
				return Expression.Constant(replacement);
			}
			return base.VisitConstant(node);
		}
	}
}
